﻿using System;
using System.Collections.Generic;
using System.Linq;
using ControleRepresentante.Logic.Models;

namespace ControleRepresentante.Logic
{
    public interface IAreaOperacaoRegistry
    {
        IEnumerable<Operacao> GetOperacoes(Area area);
        bool IsOperacaoRelevant(Operacao operacao, Representante representante);
        bool IsAreaRelevant(Area area, Representante representante);
    }
    
    public class AreaOperacaoRegistry : IAreaOperacaoRegistry
    {
        private static List<Area> m_Areas;

        public static IEnumerable<Area> GetAreas()
        {
            return m_Areas ?? (m_Areas = Enum.GetValues(typeof (Area)).Cast<Area>().Except(new[] {Area.Mock}).ToList());
        }

        public static void AddMockArea()
        {
            GetAreas();
            m_Areas.Add(Area.Mock);
        }

        private class OperacaoInfo
        {
            public Operacao[] Operacoes { get; private set; }
            public Predicate<Representante> Predicate { get; private set; }

            public OperacaoInfo(Operacao[] operacoes)
                : this(operacoes, x => true)
            {
            }

            public OperacaoInfo(Operacao[] operacoes, Predicate<Representante> predicate)
            {
                Operacoes = operacoes;
                Predicate = predicate;
            }
        }
        private readonly Dictionary<Area, OperacaoInfo> m_AreaOperacao;

        public AreaOperacaoRegistry()
        {
            m_AreaOperacao = new Dictionary<Area, OperacaoInfo>();
        }

        public void Register(Area area, params Operacao[] operacoes)
        {
            m_AreaOperacao[area] = new OperacaoInfo(operacoes);
        }

        public void Register(Area area, Predicate<Representante> predicate, params Operacao[] operacoes)
        {
            m_AreaOperacao[area] = new OperacaoInfo(operacoes, predicate);
        }

        public static void RegisterControleRepresentanteAreas(AreaOperacaoRegistry areaOperacaoRegistry)
        {
            areaOperacaoRegistry.Register(Area.Atributos, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Clientes, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Configuracoes, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Fabricas, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Pedidos, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View, Operacao.Print, Operacao.Compare);
            areaOperacaoRegistry.Register(Area.Produto, Operacao.View);
            areaOperacaoRegistry.Register(Area.RelatorioVisita, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Sync, x => x.UseJolitex, Operacao.Synchronize);
            areaOperacaoRegistry.Register(Area.Transportadoras, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Usuarios, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
            areaOperacaoRegistry.Register(Area.Vendedores, Operacao.Create, Operacao.Delete, Operacao.Update, Operacao.View);
        }

        public IEnumerable<Operacao> GetOperacoes(Area area)
        {
            return m_AreaOperacao[area].Operacoes;
        }

        public bool IsOperacaoRelevant(Operacao operacao, Representante representante)
        {
            return m_AreaOperacao.Values.Any(x => x.Operacoes.Contains(operacao) && x.Predicate(representante));
        }

        public bool IsAreaRelevant(Area area, Representante representante)
        {
            OperacaoInfo operacaoInfo;
            return m_AreaOperacao.TryGetValue(area, out operacaoInfo) && operacaoInfo.Predicate(representante);
        }
    }
}